﻿// 01_ffmpeg_player.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <Windows.h>
#include <vector>
#include <mutex>
#include <list>
#include <thread>
#include <sstream>
#include "sdl2/SDL.h"


extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
#include <libavformat/avformat.h>
}

long long NowMs()
{
	return clock() / (CLOCKS_PER_SEC / 1000);
}

using namespace std;
void MSleep(unsigned int ms)
{
	auto beg = clock();
	for (int i = 0; i < ms; ++i)
	{
		std::this_thread::sleep_for(1ms);
		if (ms <= ((clock() - beg) / (CLOCKS_PER_SEC / 1000)))
			break;
	}
}

void XFreePacket(AVPacket* pkt)
{
	av_packet_free(&pkt);
}

void XFreeFrame(AVFrame** frame)
{
	av_frame_free(frame);
}


#pragma comment(lib, "avutil.lib")
#pragma comment(lib, "avcodec.lib")
#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "sdl2.lib")

enum SYNCTYPE {
	SYNC_AUDIOMASTER=1,
	SYNC_VIDEOMASTER=2
};

struct ffPlayerState
{
	AVFormatContext* ic = nullptr;
	const char* p_input_file = nullptr;
	int n_input_vindex = -1;
	int n_input_aindex = -1;
	AVStream* p_input_vstream = nullptr;
	AVStream* p_input_astream = nullptr;

	int nWindowWidth = 0;
	int nWindowHeight = 0;
	SDL_Window* sdl_window = nullptr;
	SDL_Renderer* sdl_render = nullptr;
	SDL_Texture* sdl_texture = nullptr;

	AVCodec* p_input_vcodec = nullptr;
	AVCodecContext* p_input_vcontext = nullptr;

	AVCodec* p_input_acodec = nullptr;
	AVCodecContext* p_input_acontext = nullptr;

	bool is_exit_ = false;

	std::thread video_decode_thread_;
	std::mutex video_pkts_mutex_;
	std::list<AVPacket*> video_pkts_;
	std::mutex video_frames_mutex_;
	std::list<AVFrame*> video_frames_;
	volatile bool is_video_thread_exit_ = false;

	std::thread audio_decode_thread_;
	std::mutex audio_pkts_mutex_;
	std::list<AVPacket*> audio_pkts_;
	std::mutex audio_frames_mutex_;
	std::list<AVFrame*> audio_frames_;
	volatile bool is_audio_thread_exit_ = false;

	std::thread demux_thread_;
	volatile bool is_demux_exit_ = false;

	std::thread play_thread_;
	volatile bool is_play_thread_exit_ = false;

	///////////////////////////////////
	SYNCTYPE sync_type_ = SYNC_AUDIOMASTER;
	volatile long long cur_audio_pts_ = 0;
	volatile long long last_ms = 0;

	volatile long long cur_sync_pts = 0;
};
static ffPlayerState* pState = nullptr;

void printError(int nCode)
{
	char sError[1024] = { 0 };
	int nRet = av_strerror(nCode, sError, 1023);
	if (0 == nRet)
	{
		std::cerr << sError;
	}
	else
	{
		std::cout << "printError: " << nCode;
	}
}

#undef main

struct AudioBuffer
{
	std::vector<unsigned char> vecAudioBuffer;
	int nOffSet = 0;
	long long pts = 0;
};
std::mutex audio_buffer_mtx;
std::list< AudioBuffer> audio_datas;

void DrawVideoFrame(AVFrame* vFrame)
{
	//switch (vFrame->format)
	//{
	//case AV_PIX_FMT_YUV420P:

	//{
	//	//std::cout << "[YUV420P]" << vFrame->pkt_duration;
	//	//Sleep(vFrame->pkt_duration/10);
	//}
	//break;

	//default:
	//	break;
	//}

	int nRet = SDL_UpdateYUVTexture(pState->sdl_texture, 0,
		vFrame->data[0], vFrame->linesize[0],
		vFrame->data[1], vFrame->linesize[1],
		vFrame->data[2], vFrame->linesize[2]);
	nRet = SDL_RenderClear(pState->sdl_render);
	nRet = SDL_RenderCopy(pState->sdl_render, pState->sdl_texture, 0, 0);
	SDL_RenderPresent(pState->sdl_render);
}

void PushAudioData(const unsigned char* data, int len, long long pth)
{
	std::lock_guard<std::mutex> lock(audio_buffer_mtx);
	audio_datas.push_back(AudioBuffer());
	audio_datas.back().vecAudioBuffer.assign(data, data + len);
	audio_datas.back().nOffSet = 0;
	audio_datas.back().pts = pth;
}

void PushAudioFrame(AVFrame* frame)
{
	{
		int channels = frame->channels;
		int sample_size = av_get_bytes_per_sample((AVSampleFormat)frame->format);
		if (1 == channels)
		{
			PushAudioData(frame->data[0], frame->nb_samples*sample_size, frame->pts);
		}
		else
		{
			std::vector<unsigned char> buf;
			switch (frame->format)
			{
			case AV_SAMPLE_FMT_S32P:
			case AV_SAMPLE_FMT_FLTP:
			{
				buf.resize(frame->linesize[0]);
				unsigned char* L = frame->data[0];
				unsigned char* R = frame->data[1];
				unsigned char* data = buf.data();
				for (int i = 0; i < frame->nb_samples; ++i)
				{
					memcpy(data + i * sample_size*channels, L + i * sample_size, sample_size);
					memcpy(data + i * sample_size*channels + sample_size, R + i * sample_size, sample_size);
				}
				PushAudioData(data, frame->linesize[0], frame->pts);
				return;
			}
			
			break;
			default:
				break;
			}
		}
		PushAudioData(frame->data[0], frame->linesize[0], frame->pts);
	}
}

void my_AudioCallback(void *userdata, Uint8 * stream, int len)
{
	SDL_memset(stream, 0,len);

	{
		std::lock_guard<std::mutex> locker(audio_buffer_mtx);
		if (audio_datas.empty()) {
			if (pState->is_play_thread_exit_) {
				pState->is_exit_ = true;
				SDL_PauseAudio(1);
				std::cout << "Audio is deviced." << std::flush;
			}
			return;
		}

		AudioBuffer& data = audio_datas.front();
		pState->cur_audio_pts_ = data.pts;
		pState->last_ms = NowMs();
		int nLeft = len;
		int nCopyOffset = 0;
		while ((0 < nLeft) && !audio_datas.empty())
		{
			data = audio_datas.front();

			int nDataSize = data.vecAudioBuffer.size() - data.nOffSet;
			if (nDataSize > nLeft)
			{
				nDataSize = nLeft;
			}

			SDL_MixAudio(stream+nCopyOffset, data.vecAudioBuffer.data()+data.nOffSet, nDataSize, SDL_MIX_MAXVOLUME);
			nLeft -= nDataSize;
			nCopyOffset += nDataSize;
			data.nOffSet += nDataSize;

			if (data.vecAudioBuffer.size() <= data.nOffSet)
				audio_datas.pop_front();
		}
	}
}

long long GetCurAudioPts(ffPlayerState* player)
{
	std::lock_guard<std::mutex> locker(audio_buffer_mtx);
	double ms = 0;
	if(0 < player->last_ms)
		ms = NowMs() - player->last_ms;
	if (player->p_input_astream && (0 < player->p_input_astream->time_base.num))
	{
		ms = ms / (double)1000/(double)(player->p_input_astream->time_base.den / player->p_input_astream->time_base.num);
	}
	return player->cur_audio_pts_ + ms;
}

long long GetCurVideoPts(ffPlayerState* player)
{
	return player->cur_sync_pts;
}

bool OpenDemuxAndDecoder(ffPlayerState* pplayer)
{
	bool bRet = false;
	do
	{
		int nRet = avformat_open_input(&pplayer->ic, pplayer->p_input_file, nullptr, nullptr);
		if (0 != nRet)
		{
			printError(nRet);
			break;
		}

		nRet = avformat_find_stream_info(pplayer->ic, 0);
		if (0 != nRet)
		{
			printError(nRet);
		}

		av_dump_format(pplayer->ic, 0, pplayer->p_input_file, 0);

		std::cout << "Stream count: " << pplayer->ic->nb_streams << std::endl;
		for (int i = 0; i < pplayer->ic->nb_streams; ++i)
		{
			if (AVMEDIA_TYPE_VIDEO == pplayer->ic->streams[i]->codecpar->codec_type)
			{
				std::cout << "Video Stream index: " << pplayer->ic->streams[i]->index << std::endl;
				pplayer->n_input_vindex = pplayer->ic->streams[i]->index;
				pplayer->p_input_vstream = pplayer->ic->streams[i];
			}
			else if (AVMEDIA_TYPE_AUDIO == pplayer->ic->streams[i]->codecpar->codec_type)
			{
				std::cout << "Audio Stream index: " << pplayer->ic->streams[i]->index << std::endl;
				pplayer->n_input_aindex = pplayer->ic->streams[i]->index;
				pplayer->p_input_astream = pplayer->ic->streams[i];
			}
		}

		if (pplayer->p_input_vstream)
		{
			pplayer->p_input_vcodec = avcodec_find_decoder(pplayer->p_input_vstream->codecpar->codec_id);
			if (!pplayer->p_input_vcodec)
			{
				break;
			}
			pplayer->p_input_vcontext = avcodec_alloc_context3(pplayer->p_input_vcodec);
			if (!pplayer->p_input_vcontext)
			{
				break;
			}

			nRet = avcodec_parameters_to_context(pplayer->p_input_vcontext, pplayer->p_input_vstream->codecpar);
			if (0 != nRet)
			{
				printError(nRet);
				break;
			}

			nRet = avcodec_open2(pplayer->p_input_vcontext, pplayer->p_input_vcodec, 0);
			if (0 != nRet)
			{
				printError(nRet);
				break;
			}

			pplayer->nWindowWidth = pplayer->p_input_vstream->codecpar->width;
			pplayer->nWindowHeight = pplayer->p_input_vstream->codecpar->height;
			std::cout << "width: " << pplayer->nWindowWidth << ",height: " << pplayer->nWindowHeight;
		}

		if (pplayer->p_input_astream)
		{
			pplayer->p_input_acodec = avcodec_find_decoder(pplayer->p_input_astream->codecpar->codec_id);
			if (!pplayer->p_input_acodec)
			{
				std::cout << "avcodec_find_decoder audio failed" << std::endl;
				break;
			}

			pplayer->p_input_acontext = avcodec_alloc_context3(pplayer->p_input_acodec);
			if (!pplayer->p_input_acontext)
			{
				std::cout << "avcodec_alloc_context3 failed" << std::endl;
				break;

			}

			nRet = avcodec_parameters_to_context(pplayer->p_input_acontext, pplayer->p_input_astream->codecpar);
			if (0 != nRet)
			{
				printError(nRet);
				break;
			}

			nRet = avcodec_open2(pplayer->p_input_acontext, pplayer->p_input_acodec, 0);
			if (0 != nRet)
			{
				printError(nRet);
				break;
			}
		}
		bRet = true;
	} while (0);

	return bRet;
}

void VideoDecodeThreadFun(ffPlayerState* pplayer)
{
	AVPacket* previousPkt = nullptr;
	pState->is_video_thread_exit_ = false;
	while (!pState->is_exit_)
	{
		AVPacket* pkt = nullptr;
		bool bGet = false;
		do
		{
			if (nullptr != previousPkt)
			{
				pkt = previousPkt;
				previousPkt = nullptr;
				bGet = true;
				break;
			}
			
			std::lock_guard<std::mutex> lock(pState->video_pkts_mutex_);
			if (pState->video_pkts_.empty())
			{
				break;
			}
			pkt = pState->video_pkts_.front();
			pState->video_pkts_.pop_front();
			bGet = true;
		} while (0);
		if (!bGet)
		{
			MSleep(2);
			continue;
		}

		std::cout << "@" << std::flush;
		int nRet = avcodec_send_packet(pState->p_input_vcontext, pkt);
		if (0 != nRet)
		{
			previousPkt = pkt;
			MSleep(1);
			continue;
		}
		if(pkt)
			XFreePacket(pkt);

		do{
			AVFrame* vFrame = av_frame_alloc();
			nRet = avcodec_receive_frame(pState->p_input_vcontext, vFrame);
			if (0 != nRet)
			{
				XFreeFrame(&vFrame);
				break;
			}

			{
				std::lock_guard<std::mutex> lock(pState->video_frames_mutex_);
				pState->video_frames_.push_back(vFrame);
				std::cout << "V" << std::flush;
			}
		} while (!pkt);

		if (!pkt) {
			break;
		}
		MSleep(1);
	}
	pState->is_video_thread_exit_ = true;
	std::cout << "Video decode Thread Quit." << std::endl;
}
bool StartVideoDecodeThread(ffPlayerState* pplayer)
{
	bool bRet = false;
	do
	{
		if (-1 == pplayer->n_input_vindex || !pplayer->p_input_vstream)
		{
			pplayer->is_video_thread_exit_ = true;
			break;
		}
		pplayer->video_decode_thread_ = std::thread(VideoDecodeThreadFun, pState);
		bRet = true;
	} while (0);
	return true;
}

void AudioDecodeThreadFun(ffPlayerState* pplayer)
{
	AVPacket* previousPkt = nullptr;
	pState->is_audio_thread_exit_ = false;
	while (!pState->is_exit_)
	{
		AVPacket* pkt = nullptr;
		bool bGet = false;
		do{
			if (nullptr != previousPkt)
			{
				pkt = previousPkt;
				bGet = true;
				break;
			}

			std::lock_guard<std::mutex> lock(pState->audio_pkts_mutex_);
			if (pState->audio_pkts_.empty())
			{
				break;
			}
			pkt = pState->audio_pkts_.front();
			pState->audio_pkts_.pop_front();
			bGet = true;
		} while (0);
		if (!bGet)
		{
			MSleep(1);
			continue;
		}

		std::cout << "#" << std::flush;
		int nRet = avcodec_send_packet(pState->p_input_acontext, pkt);
		if (0 != nRet)
		{
			previousPkt = pkt;
			MSleep(1);
			continue;
		}
		if(pkt)
			XFreePacket(pkt);
		
		do{
			AVFrame* frame = av_frame_alloc();
			nRet = avcodec_receive_frame( pState->p_input_acontext, frame);
			if (0 != nRet)
			{
				XFreeFrame(&frame);
				break;
			}
			{
				std::lock_guard<std::mutex> lock(pState->audio_frames_mutex_);
				pState->audio_frames_.push_back(frame);
			}
		} while (!pkt);
		if (!pkt)
		{
			break;
		}

		MSleep(1);
	}
	pState->is_audio_thread_exit_ = true;
	std::cout << "Audio decode thread Quit." << std::endl;
}

bool StartAudioDecodeThread(ffPlayerState* pplayer)
{
	bool bRet = false;
	do
	{
		if (-1 == pplayer->n_input_aindex || !pplayer->p_input_astream) {
			pplayer->is_audio_thread_exit_ = true;
			break;
		}

		pplayer->audio_decode_thread_ = std::thread(AudioDecodeThreadFun, pState);
		bRet = true;
	} while (0);
	return true;
}

void DemuxThreadFun(ffPlayerState* player)
{
	int nRet = 0;

	pState->is_demux_exit_ = false;
	AVPacket pkt;
	while (!pState->is_exit_)
	{
		nRet = av_read_frame(pState->ic, &pkt);
		if (0 != nRet)
		{
			printError(nRet);
			break;
		}

		AVPacket* tmpPkt = av_packet_alloc();
		if (0 == av_packet_ref(tmpPkt, &pkt))
		{
			if (pState->n_input_vindex == pkt.stream_index)
			{
				std::cout << "-" << std::flush;
				std::lock_guard<std::mutex> lock(pState->video_pkts_mutex_);
				pState->video_pkts_.push_back(tmpPkt);
			}
			else if (pState->n_input_aindex == pkt.stream_index)
			{
				std::cout << "+" << std::flush;
				std::lock_guard<std::mutex> lock(pState->audio_pkts_mutex_);
				pState->audio_pkts_.push_back(tmpPkt);
			}
		}
		else
		{
			av_packet_free(&tmpPkt);
		}

		av_packet_unref(&pkt);
		MSleep(1);
	}

	AVPacket* tmpPkt = nullptr;
	{
		std::cout << "-" << std::flush;
		std::lock_guard<std::mutex> lock(pState->video_pkts_mutex_);
		pState->video_pkts_.push_back(tmpPkt);
	}
	{
		std::cout << "+" << std::flush;
		std::lock_guard<std::mutex> lock(pState->audio_pkts_mutex_);
		pState->audio_pkts_.push_back(tmpPkt);
	}
	pState->is_demux_exit_ = true;
	std::cout << "Demux Thread Quit." << std::endl;
}

bool StartDemuxThread(ffPlayerState* pplayer)
{
	bool bRet = false;

	do
	{
		pplayer->demux_thread_ = std::thread(DemuxThreadFun, pState);
		bRet = true;
	} while (0);
	return bRet;
}

bool OpenVideoAndAudioDevice(ffPlayerState* pplayer)
{
	bool bRet = false;
	do
	{
		SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);
		if (pplayer->p_input_vstream)
		{
			pplayer->sync_type_ = SYNC_VIDEOMASTER;
			pplayer->sdl_window = SDL_CreateWindow("Video Test", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
				pplayer->nWindowWidth > 0 ? pplayer->nWindowWidth : 640,
				pplayer->nWindowHeight > 0 ? pplayer->nWindowHeight : 480,
				SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
			if (!pplayer->sdl_window)
			{
				std::cout << "sdl_createwindow failed." << std::endl;
				break;
			}

			pplayer->sdl_render = SDL_CreateRenderer(pplayer->sdl_window, -1, SDL_RENDERER_ACCELERATED);
			if (!pplayer->sdl_render)
			{
				std::cout << "sdl create render failed" << std::endl;
				break;
			}
			pplayer->sdl_texture = SDL_CreateTexture(pplayer->sdl_render, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, pplayer->nWindowWidth > 0 ? pplayer->nWindowWidth : 640,
				pplayer->nWindowHeight > 0 ? pplayer->nWindowHeight : 480);
			if (!pplayer->sdl_texture)
			{
				std::cout << "sdl create texture failed" << std::endl;
				break;
			}
		}

		/////////////////////////
		if (pplayer->p_input_astream)
		{
			pplayer->sync_type_ = SYNC_AUDIOMASTER;
			SDL_AudioSpec sdl_as;
			sdl_as.channels = pplayer->p_input_astream->codecpar->channels;

			sdl_as.freq = pplayer->p_input_astream->codecpar->sample_rate;
			sdl_as.silence = 0;
			switch (pplayer->p_input_astream->codecpar->format)
			{
			case AV_SAMPLE_FMT_S16:
			case AV_SAMPLE_FMT_S16P:
				sdl_as.format = AUDIO_S16;
				break;
			case AV_SAMPLE_FMT_S32:
			case AV_SAMPLE_FMT_S32P:
				sdl_as.format = AUDIO_S32;
				break;
			case AV_SAMPLE_FMT_FLT:
			case AV_SAMPLE_FMT_FLTP:
				sdl_as.format = AUDIO_F32;
				break;
			default:
				break;
			}

			sdl_as.callback = my_AudioCallback;
			sdl_as.userdata = 0;
			sdl_as.samples = 1024;
			SDL_AudioSpec sdl_ob;
			int nRet = SDL_OpenAudio(&sdl_as, &sdl_ob);
			if (0 != nRet)
			{
				printError(nRet);
				break;
			}

			SDL_PauseAudio(0);
		}
		bRet = true;
	} while (0);
	
	return bRet;
}

void PlayThreadFunc(ffPlayerState* player)
{
	pState->is_play_thread_exit_ = false;
	while (!pState->is_exit_)
	{
		do
		{
			if (-1 == pState->n_input_aindex || !pState->p_input_astream)
				break;
				
			std::lock_guard<std::mutex> lock(pState->audio_frames_mutex_);
			if (pState->audio_frames_.empty())
				break;

			std::cout << "A" << std::flush;
			auto frame = pState->audio_frames_.front();
			pState->audio_frames_.pop_front();
			PushAudioFrame(frame);
			XFreeFrame(&frame);
		} while (0);

		do
		{
			if (-1 == pState->n_input_vindex || !pState->p_input_vstream)
				break;

			std::lock_guard<std::mutex> lock(pState->video_frames_mutex_);
			if (pState->video_frames_.empty())
				break;

			std::cout << "V" << std::flush;
			auto frame = pState->video_frames_.front();

			{
				if (SYNC_AUDIOMASTER == pState->sync_type_)
				{
					long long cur_sync_pts_video = av_rescale_q(pState->cur_sync_pts, pState->p_input_astream->time_base, pState->p_input_vstream->time_base);
					{
						std::stringstream oss;
						oss << "{" << frame->pts
							<< "," << frame->pkt_duration*1000*pState->p_input_vstream->time_base.num/pState->p_input_vstream->time_base.den//cur_sync_pts_video
							<< "}";
						std::cout << oss.str() << std::flush;
					}
					if (frame->pts <= cur_sync_pts_video)
					{
						pState->video_frames_.pop_front();
						DrawVideoFrame(frame);
						XFreeFrame(&frame);
					}
				}
				else if (SYNC_VIDEOMASTER == pState->sync_type_)
				{
					if (!pState->p_input_astream)
					{
						do
						{
							long long cur_ms = NowMs();
							long long duration = 0;
							if (0 == pState->cur_sync_pts)
							{
								if (0 < pState->p_input_vstream->time_base.den)
									duration = frame->pkt_duration * 1000 * pState->p_input_vstream->time_base.num / pState->p_input_vstream->time_base.den;
							}
							else
							{
								if (pState->cur_sync_pts <= cur_ms - pState->last_ms)
								{
									if (0 < pState->p_input_vstream->time_base.den)
										duration = frame->pkt_duration * 1000 * pState->p_input_vstream->time_base.num / pState->p_input_vstream->time_base.den;
								}
								else
								{
									break;
								}
							}

							pState->cur_sync_pts = duration;
							pState->last_ms = cur_ms;
							pState->video_frames_.pop_front();
							DrawVideoFrame(frame);
							XFreeFrame(&frame);
						} while (0);
					}
				}
			}
		} while (0);

		MSleep(5);

		if (pState->is_audio_thread_exit_ && pState->is_video_thread_exit_ && pState->audio_frames_.empty() && pState->video_frames_.empty())
			break;
	}
	if (!pState->p_input_astream)
	{
		pState->is_exit_ = true;
	}
	pState->is_play_thread_exit_ = true;
	std::cout << "Play Thread Quit." << std::endl;
}

bool StartPlayThread(ffPlayerState* player)
{
	bool bRet = false;
	do {
		player->play_thread_ = std::thread(PlayThreadFunc, pState);
		bRet = true;
	} while (0);
	return bRet;
}

void CloseFFMPEG(ffPlayerState* player)
{
	if (player->p_input_acontext)
	{
		avcodec_free_context(&player->p_input_acontext);
	}
	if (player->p_input_vcontext)
	{
		avcodec_free_context(&player->p_input_vcontext);
	}

	if (player->ic)
	{
		avformat_close_input(&player->ic);
	}
	player->n_input_aindex = -1;
	player->n_input_vindex = -1;
	player->p_input_acodec = nullptr;
	player->p_input_astream = nullptr;
	player->p_input_vcodec = nullptr;
	player->p_input_vstream = nullptr;
}

void CloseSDL(ffPlayerState* player)
{
	if (player->sdl_texture)
	{
		SDL_DestroyTexture(player->sdl_texture);
		player->sdl_texture = nullptr;
	}
	if (player->sdl_render)
	{
		SDL_DestroyRenderer(player->sdl_render);
		player->sdl_render = nullptr;
	}
	if (player->sdl_window)
	{
		SDL_DestroyWindow(player->sdl_window);
		player->sdl_window = nullptr;
	}

	SDL_Quit();
}

void UpdateSync(ffPlayerState* player)
{
	long long cur_sync_pts = 0;
	if (SYNC_AUDIOMASTER == player->sync_type_)
		cur_sync_pts = GetCurAudioPts(player);
	else if (SYNC_VIDEOMASTER == player->sync_type_)
		cur_sync_pts = GetCurVideoPts(player);
	player->cur_sync_pts = cur_sync_pts;

}

int main()
{
	std::cout << avutil_configuration();

	pState = new ffPlayerState();
	pState->p_input_file = "400_300_25.mp4";

	bool bInit = false;
	do
	{
		if (!OpenDemuxAndDecoder(pState))
		{
			std::cout << "Open demux and decoder failed..." << std::endl;
			break;
		}

		if (!OpenVideoAndAudioDevice(pState))
		{
			std::cout << "open video and audio failed..." << std::endl;
			break;
		}

		if (!StartVideoDecodeThread(pState))
		{
			std::cout << "start video decode thread failed..." << std::endl;
			break;
		}

		if (!StartAudioDecodeThread(pState))
		{
			std::cout << "start audio decode thread failed..." << std::endl;
			break;
		}

		if (!StartDemuxThread(pState))
		{
			std::cout << "start demux thread failed..." << std::endl;
			break;
		}

		if (!StartPlayThread(pState))
		{
			std::cout << "start play thread failed..." << std::endl;
			break;
		}

		bInit = true;
	} while (0);
	
	long long ms0 = NowMs();
	while (1)
	{
		if (bInit && pState->is_exit_)
		{
			break;
		}
		//SDL_Event evt;
		//if (0 == SDL_WaitEventTimeout(&evt, 2))
		//{
		//	pState->is_exit_ = true;
		//	break;
		//}
		UpdateSync(pState);
		MSleep(1);
	}

	long long ms1 = NowMs();
	std::cout << std::endl;
	std::cout << "Elapsed time: " << ms1 - ms0 << std::flush;

	MSleep(500);
	CloseSDL(pState);
	CloseFFMPEG(pState);
	system("pause");
	return 0;
	
}

// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单

// 入门使用技巧: 
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件，或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来，若要再次打开此项目，请转到“文件”>“打开”>“项目”并选择 .sln 文件
